package main

import (
	"fmt"
	"io"
	"log"
	"math"
	"math/rand"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"
	_ "net/http/pprof"
	"encoding/csv"
	"github.com/google/uuid"
	_"github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

func RandInts(min, max int) int {
	// if max < min {
	// 	max = max - min
	// 	min = max + min
	// 	max = min - max
	// }
	rr := rand.New(rand.NewSource(time.Now().UnixNano() * rand.Int63n(9999)))
	return rr.Intn(max - min + 1) + min
}

func alloc(score, count, min, max int)[]int{
	fmt.Println("alloc0", score, count, min, max)
	var retlist []int
	score -= min*count
	for count > 1 {
		maxRemain := score/count*2
		if maxRemain > max {
			maxRemain = max
		}
		if maxRemain < min {
			maxRemain = min + 1
		}
		//fmt.Println("maxRemain", maxRemain, score, count, min)
		ret := RandInts(0, maxRemain - min)
		score -= ret
		count --
		num := ret + min
		if num <= 0 {
			fmt.Println("alloc", maxRemain, score, count, min, max)
			num = RandInts(1, 3)
		}
		retlist = append(retlist, num)
	}
	num := score + min
	if num <= 0 {
		fmt.Println("alloc2", score, count, min, max)
		num = RandInts(1, 3)
	}
	retlist = append(retlist, num)
	return retlist
}

func floattostr(input_num float64) string {
	return strconv.FormatFloat(input_num, 'g', 1, 32)
}

func timeformal(timestr string)(string, string){
	tims := strings.Split(timestr, "/")
	ret := tims[0] + "/"
	if len(tims[1]) <= 1 {
		ret += "0" + tims[1]
	} else {
		ret += tims[1]
	}
	ret += "/"
	if len(tims[2]) <= 1 {
		ret += "0" + tims[2]
	} else {
		ret += tims[2]
	}
	return ret, tims[1]
}

func getRandTimeByWeight(count int)int{
	timeweight := [...]int{85,66,52,43,33,22,11,15,20,25,28,30,38,40,38,55,60,65,63,68,71,75,70,66,75,80,85,90,100,90,80,75,66,68,74,66,88,80,95,88,90,100,100,100,80,70,75,80}
	if count <= 100 {
		for i := 0; i< 48; i++ {
			if timeweight[i] < 70 {
				timeweight[i] = 0
			}
		}
	}
	
	for i := 1; i< 48; i++ {
		timeweight[i] += timeweight[i-1]
	}
	ret := RandInts(1, timeweight[47])
	dex := 0
	for i := 0; i< 48; i++ {
		if ret <= timeweight[i] {
			dex = i
			break
		}
	}
	return dex
}

func getTimeRand(timestr string, count int)time.Time{
	tm2, _ := time.Parse("2006/01/02", timestr)
	cur := tm2.Unix()
	min := getRandTimeByWeight(count)*1800
	ret := int(cur-8*3600) + RandInts(min, min+1800)
	retu := time.Unix(int64(ret), 0)
	return retu
}

//唯一id
func CreateRefId(playType int) string {
	serverId := RandInts(1, 3)
	return strconv.Itoa(int(serverId)) + "_" + strconv.Itoa(int(playType)) + "_" + uuid.New().String()
}

type tosql struct {
	createdAt       	time.Time   `json:"createdAt" db:"createdAt"`
	level  				float32 	`json:"level" db:"level"`
	platformCommission  float32  `json:"platformCommission" db:"platformCommission"`
}

type tosqlLs []*tosql
func (p tosqlLs) Len() int           { return len(p) }
func (p tosqlLs) Less(i, j int) bool { return (*p[i]).createdAt.Before((*p[j]).createdAt) }
func (p tosqlLs) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

type UserData struct {
	Id       int       `json:"id" db:"id"`
	RegTime  time.Time `json:"regTime" db:"regTime"`
	Region   string    `json:"region" db:"region"`
	PlayType int       `json:"playType" db:"playType"`
}

var (
	database     *sqlx.DB //mysql连接对象//
	trandata 	chan UserData
)


//连接mysql
func initMysql() {
	trandata = make(chan UserData, 100)
	fmt.Printf("StartDB...")
	var err error
	//Log.Printf("mysql config, host:%s, name:%s, name:%s", MysqlConfigViper_.GetString("host"), MysqlConfigViper_.GetString("name"), MysqlConfigViper_.GetString("username"))
	connStr := fmt.Sprintf("%s:%s@tcp(%s)/%s?parseTime=true&loc=Local&charset=utf8",
		"root", "f3bc7ec833F98e491939e74a90d875", "120.76.201.244:3306", "indiaGame")
	database, err = sqlx.Connect("mysql", connStr)
	if err != nil {
		panic(err)
	}
	database.DB.SetMaxOpenConns(500)
	database.DB.SetMaxIdleConns(100)
	database.DB.SetConnMaxLifetime(time.Second * 0)
	fmt.Printf("The Db is: %v \n", database)
}

//取用户信息
func queryUser(playType int, limit int, offset int, back func(*UserData)){
	sql := fmt.Sprintf("select * from india_user where playType=%d and regTime > '2020-11-11' limit %d offset %d", playType, limit, offset)

	results, err := database.Query(sql)
	if err != nil {
		print(err.Error())
		return
	}
	//fmt.Println("sql:",sql, results)
	for results.Next() {
		var data UserData
		err = results.Scan(&data.Id, &data.RegTime, &data.Region, &data.PlayType)
		if err != nil {
			print(err.Error())
			return
		}
		del := (RandInts(0,8) - data.Id%9 + 9)%9
		if del < 4 {
			back(&data)
		}
	}
}

var userPool []UserData
var 	userOffset int
//查户信息
func queryUser2(createTime string, createTime2 string, playType int, limit int, offset int) []UserData{
	sql := fmt.Sprintf("select * from india_user where playType=%d and regTime < '%s' and regTime > '%s' limit %d offset %d ", playType, createTime, createTime2, limit, offset)
	results, err := database.Query(sql)
	//fmt.Println("sql ",sql)
	if err != nil {
		fmt.Println("sql ",sql)
		panic(err.Error())
	}
	userArr := []UserData{}
	for results.Next() {
		var data UserData
		err = results.Scan(&data.Id, &data.RegTime, &data.Region, &data.PlayType)
		if err != nil {
			//panic(err.Error())
			fmt.Println("ERROR scan ", err)
			continue
		}
		userArr = append(userArr, data)
	}
	return userArr
}
func getUser(date int64) UserData{
	dateStr := time.Unix(int64(date - 180), 0).Format("2006-01-02 15:04:05")
	date2Str := time.Unix(int64(date - 50*86400), 0).Format("2006-01-02 15:04:05")
	if len(userPool) == 0 {
		limit := RandInts(300, 3000)
		//limit := offset * RandInts(2,10)
		userPool = queryUser2(dateStr, date2Str, 110, limit, userOffset)
		userOffset =  RandInts(0, 200)
		//fmt.Println("userOffset", userOffset)
		for {
			if len(userPool) == 0 {
				userOffset = userOffset - 100
				if userOffset < 0 {
					userOffset = 0
					limit = 100
				}
			}else {
				break
			}
			userPool = queryUser2(dateStr, date2Str,110, limit, userOffset)
			if userOffset == 0 {
				break
			}
		}
		//打乱数据
		for i := len(userPool) - 1; i > 0; i-- {
			num := rand.Intn(i + 1)
			userPool[i], userPool[num] = userPool[num], userPool[i]
		}
	}
	maxSize := len(userPool)
	for i := 0; i < maxSize; i++ {
		user := userPool[0]
		userPool = append(userPool[1:])
		maxSize = len(userPool)
		if user.RegTime.Unix() + 3*60 > int64(date) {
			//fmt.Println("___", user.RegTime.Unix() + 3*60, date)
			continue
		}
		return user
	}
	if len(userPool) == 0 {
		userPool = queryUser2(dateStr, date2Str,110, 100, 0)
		//打乱数据
		for i := len(userPool) - 1; i > 0; i-- {
			num := rand.Intn(i + 1)
			userPool[i], userPool[num] = userPool[num], userPool[i]
		}
	}
	//fmt.Println("___date", len(userPool), date)
	return userPool[len(userPool) - 1]
}

type InData struct {
	CreatedAt          time.Time `json:"createdAt" db:"createdAt"`
	Uid                int       `json:"uid" db:"uid"`
	Region             string    `json:"region" db:"region"`
	PlayType           int       `json:"playType" db:"playType"`
	Level              float32   `json:"level" db:"level"`
	PlatformCommission float32   `json:"platformCommission" db:"platformCommission"`
	RefId              string    `json:"refId" db:"refId"`
}

func main(){
	initMysql()

	fileName := "texas202101.csv"
	//mainwait(fileName)

	checkVeryDate(fileName)
}

//
////取随机时间
//func getRandTime(date string) int{
//	arr := strings.Split(date, "/")
//	str := ""
//	for _, v := range arr {
//		if len(v) == 1 {
//			v = "0" + v
//		}
//		str = str + "/" + v
//	}
//	str = strings.TrimLeft(str, "/")
//	//fmt.Println("strDate", str)
//
//	//随机时间
//	timeObj, _ := time.Parse("2006/01/02", str)
//	//fmt.Println("timeNum", timeObj, timeObj.Unix())
//	timeNum := timeObj.Unix()
//	min := getRandTimeByWeight()*1800
//	ret := int(timeNum-8*3600) + RandInts(min, min+1800)
//	return ret
//}
//
////取一天的时间列表
//func getTimeList(date string, num int) (timeList []int) {
//	for i := 0; i < num; i++ {
//		t := getRandTime(date)
//		timeList = append(timeList, t)
//	}
//	sort.Ints(timeList)
//	return timeList
//}
//
//var 	timePool []int
//func getTime(date string, num int) int {
//	if len(timePool) == 0 {
//		timePool = getTimeList(date, num)
//	}
//	timeNum := timePool[0]
//	timePool = append(timePool[1:])
//	//fmt.Println("getTime", time.Unix(int64(timeNum),0))
//	//第一个用户注册时间 2019-12-04 00:05:24
//	userRegTimeMin := 1575389124
//	if timeNum < userRegTimeMin + 3*60 {
//		timeNum = userRegTimeMin + RandInts(3*60, 10*60)
//	}
//	return timeNum
//}

//1547565  421086
func mainwait(fileName string) {
	// file, _ := os.OpenFile("exeout.csv", os.O_WRONLY|os.O_CREATE, os.ModePerm)
	// w := csv.NewWriter(file)

	betstr := [...]float32{0.1,0.5,1,2,5,10,25,50}
	mins := [...]int{1,1,10,10,1,1,1,1}
	maxs := [...]int{24,120,240,480,120,200,400,500}

	rfile, _ := os.Open(fileName)
	r := csv.NewReader(rfile)
	arr := [...]int{6,11,16,21,26,31,36,41}
	sumcount := 0
	timestart := time.Now().Unix()
	for i := 0;;i++ {
		strs, ok := r.Read()
		if ok != nil {
			break
		}
		if len(strs[0]) < 4 {
			//日期不对
			continue
		}
		if i > 1 {
			day, month := timeformal(strs[0])
			fmt.Println(month, day)
			tolist := make([]*tosql, 0, 1000)
			sql := "insert into india_round_texas (createdAt, uid, region, playType, level, platformCommission, refId)" +
			"values(:createdAt, :uid, :region, :playType, :level, :platformCommission, :refId)"
			for j := 0; j< len(arr); j++ {
				ind := arr[j]
				strs[ind] = strings.Trim(strs[ind], " ")
				if strs[ind] == "0" || strs[ind] == "" {
					//out += "0 0 "
				} else {
					salary := strs[ind]
					intnum := 0
					devide := float32(1.0)
					if ind < 23 {
						snum, _ := strconv.ParseFloat(salary,32)
						intnum = int(math.Floor(snum*10 + 0.5))
						devide = float32(10.0)
					} else {
						//intnum, _ = strconv.Atoi(salary)
						snum, _ := strconv.ParseFloat(salary,32)
						intnum = int(math.Floor(snum))
						if intnum < 1 {
							intnum = 1
						}
					}

					count, _ := strconv.Atoi(strs[ind+1])
					if intnum == 0 {
						fmt.Println("salary", salary)
					}
					arrind := ind/5 - 1
					retlist := alloc(intnum, count, mins[arrind], maxs[arrind])

					for retInd := 0; retInd < len(retlist); retInd++ {
						//out := []string{getTimeRand(day)}
						//outlog := append(out, betstr[arrind], float(retlist[retInd])/devide)
						tolist = append(tolist, &tosql{getTimeRand(day, len(retlist)), betstr[arrind], float32(retlist[retInd])/devide})
						//w.Write(outlog)
					}
				}
			}

			sort.Sort(tosqlLs(tolist))
			for _, todata := range tolist {
				for{
					//data := <-trandata

					data := getUser(int64(todata.createdAt.Unix()))
					//fmt.Println("data:", data)
					if data.RegTime.Unix() + 137 < todata.createdAt.Unix() && data.RegTime.Unix() + 55*86400 > todata.createdAt.Unix() {
						fid := CreateRefId(110)
						gdata := InData{todata.createdAt, data.Id,  data.Region, 110, todata.level, todata.platformCommission, fid}
						_, err := database.NamedExec(sql, gdata)
						if err != nil {
							fmt.Printf("writeData ERROR sql:%v, err:%+v \n", sql, err)
							return
						}
						//fmt.Printf("writeData success sql:%+v \n", sql)
						sumcount ++
						break
					}
				}
			}
			fmt.Printf("[%d]sumcount:%d time:%d\n", i, sumcount, time.Now().Unix() - timestart)
		}
	}

	fmt.Println("All Success out!!")
	// w.Flush()
	// file.Close()
}

// SELECT DATE_FORMAT(createdAt, '%d/%m/%y %H:%i:%s'), uid, region, playType, level FROM user_round WHERE createdAt >= '2020-01-01' into OUTFILE "/tmp/test1.csv"
// fields terminated by ',' optionally enclosed by '"' escaped by '"' 
// lines terminated by '\n';



//检查每天的差额
func checkVeryDate(fileName string) {

	fmt.Println("insertData")
	//准备读取文件
	fs, err := os.Open(fileName)
	if err != nil {
		log.Fatalf("can not open the file, err is %+v", err)
	}
	defer fs.Close()
	r := csv.NewReader(fs)

	//针对大文件，一行一行的读取文件
	var  allPlatformCommission float64
	//for i:=0; i<4; i++ {
	for i := 0; ; i++ {
		row, err := r.Read()
		if err != nil && err != io.EOF {
			log.Fatalf("can not read, err is %+v", err)
		}
		if err == io.EOF {
			break
		}
		if i < 2 {
			continue
		}
		dateStr := row[0]
		chargeStr := row[1]
		chargeNum, _ := strconv.ParseFloat(chargeStr,2)
		if chargeNum <= 0 {
			continue
		}
		//if dateStr == "2020/1/1"{
		//	break
		//}
		fmt.Printf("[day: %v, %v] %v\n", i-1, row[0], row[1])
		dateStr1 := dateStr + " 00:00:00"
		dateStr2 := dateStr + " 23:59:59"
		sql := fmt.Sprintf("select sum(platformCommission) from india_round_texas where createdAt >= '%s' and createdAt <= '%s'", dateStr1, dateStr2)
		fmt.Println("sql ", sql)
		results, err := database.Query(sql)
		if err != nil {
			fmt.Printf("checkVeryDate ERROR sql:%v, err:%+v \n", sql, err)
			return
		}
		var data float64
		for results.Next() {
			err = results.Scan(&data)
			if err != nil {
				panic(err.Error())
				fmt.Println("ERROR scan ", err)
				continue
			}
			break
		}
		results.Close()
		allPlatformCommission = allPlatformCommission + data
		delayNum := int(data) - int(chargeNum)
		if delayNum == 0 {
			continue
		}

		idArr := []int{}
		//大于0 或小于0
		fmt.Println("date delay:", delayNum)
		sql = fmt.Sprintf("SELECT id FROM india_round_texas WHERE createdAt >= '%s' and createdAt < '%s' and level>1  limit %d ", dateStr1, dateStr2, int(math.Abs(float64(delayNum)/5)))
		if delayNum > 0 {
			sql = fmt.Sprintf("SELECT id FROM india_round_texas WHERE createdAt >= '%s' and createdAt < '%s' and platformCommission>5.01 limit %d ", dateStr1, dateStr2, int(math.Abs(float64(delayNum)) / 5 ))
		}
		results, err = database.Query(sql)
		//fmt.Println("sql ", sql)
		if err != nil {
			fmt.Println("sql ", sql)
			panic(err.Error())
		}
		var id int
		count := 0
		for results.Next() {
			err = results.Scan(&id)
			if err != nil {
				panic(err.Error())
				fmt.Println("ERROR scan ", err)
				continue
			}
			idArr = append(idArr, id)
			count = count + 1
		}
		results.Close()
		fmt.Println("idArr", len(idArr))
		if len(idArr) == 0 {
			continue
		}
		//对指定id 进行 platformCommission + -
		for _, id := range idArr {
			if delayNum > 0 {
				//-1
				sql = fmt.Sprintf("update india_round_texas set platformCommission=platformCommission-5 where id=%d", id)
			}else if delayNum < 0 {
				//+1
				sql = fmt.Sprintf("update india_round_texas set platformCommission=platformCommission+5 where id=%d", id)
			}
			results, err := database.Query(sql)
			if err != nil {
				fmt.Printf("writeRummyData ERROR sql:%v, err:%+v \n", sql, err)
				return
			}
			results.Close()

			//fmt.Println(sql, i)
		}



	}
	fmt.Println("allPlatformCommission", allPlatformCommission)



}
